#include <cstdlib>
#include <ctime>

#include <Geex/graphics/geexapp.h>
#include <Geex/basics/file_system.h>
#include <glut_viewer/tweak_bar.h>
#include <glut_viewer/glut_viewer.h>
#include <Geex/third_party/misc/gl2ps.h>

#include <CGAL/Timer.h>

#include "particle_packing_aggregator.h"
#include "../common/error_info.h"
//#include "error_info.h"

namespace Particle_packing {
		
	using namespace Geex;

	void TW_CALL tw_optimize(void *clientData);
	void TW_CALL tw_perturb(void*);
	void TW_CALL tw_shuffle(void*);
	void TW_CALL tw_pack(void*);
	void TW_CALL tw_print_pdf(void*);

	//void TW_CALL tw_feature_set_callback(const void*, void *);
	//void TW_CALL tw_feature_get_callback(void*, void*);
	//void TW_CALL tw_curvature_get_callback(void*, void*);
	//void TW_CALL tw_curvature_set_callback(const void*, void*);

	
    class Particle_packing_application : public GeexApp 
	{
    public:
        Particle_packing_application(int argc, char** argv) : GeexApp(argc, argv) 
		{ 
            hdr_ = false ;
	
			if (argc < 2)
				throw Bad_input("No circle file input.");
           // get_arg("nb_iter", nb_iter_) ;
			circle_filename = argv[1];
			std::cout<<"Used "<<circle_filename<<" as input.\n";
			nb_trials = 50;
			circle_union = false;
			std::string choice;
			get_arg("circle_union", choice);
			if (choice == "Yes" || choice == "True")
				circle_union = true;
		}

        Particle_packing_aggregator* particle_packing_context() { return static_cast<Particle_packing_aggregator*>(scene()) ; }

        void init_scene() 
		{
			scene_ = new Particle_packing_aggregator(circle_filename, circle_union);
        }

		void optimize()
		{
			particle_packing_context()->energy_minimize();
			glut_viewer_redraw();
		}
		void perturb()
		{
			particle_packing_context()->perturb();
			glut_viewer_redraw();
		}
		void pack()
		{
			particle_packing_context()->pack(nb_trials);
			glut_viewer_redraw();
		}
		void shuffle()
		{
			particle_packing_context()->shuffle();
			glut_viewer_redraw();
		}
        void init_gui() 
		{
            GeexApp::init_gui() ;
				
			TwBar* function_bar = TwNewBar("Functions");
			TwDefine("Functions position='16 10' size='200 150' alpha=200");
			//TwAddVarRW(function_bar, "Iter Number.", TW_TYPE_INT32, &spm()->setPackIterLim(), "min=1");
			TwAddButton(function_bar, "Optimize", tw_optimize, NULL, "key=k");
			TwAddButton(function_bar, "Perturb", tw_perturb, NULL, "key=p");
			TwAddButton(function_bar, "Shuffle", tw_shuffle, NULL, "key=s");
			TwAddVarRW(function_bar, "Times", TW_TYPE_UINT32, &nb_trials, "min=1");
			TwAddButton(function_bar, "Pack", tw_pack, NULL, "key=P");
			TwAddButton(function_bar, "PDF", tw_print_pdf, NULL, "key=f");
            //TwBar* graphics_bar = TwNewBar("Graphics");
			//TwDefine("Graphics position='16 10' size='200 250' alpha=200"); 
			//TwAddVarRW(graphics_bar, "Domain Mesh", TW_TYPE_BOOL8, &spm()->show_domain_mesh(), "");

	
			//toggle_skybox_CB() ;
			*glut_viewer_is_enabled_ptr(GLUT_VIEWER_BACKGROUND) = GL_FALSE;
			viewer_properties_->visible() = GL_FALSE;
        }

    private:
		std::string circle_filename;
		bool circle_union;
		unsigned int nb_trials;
    } ;


Particle_packing::Particle_packing_application* particle_packing_app() { return static_cast<Particle_packing::Particle_packing_application*>(Geex::GeexApp::instance()) ; }

void TW_CALL tw_optimize(void *clientData) { particle_packing_app()->optimize(); }
void TW_CALL tw_perturb(void *clientData)	{ particle_packing_app()->perturb(); }
void TW_CALL tw_pack(void *clientData)	{ particle_packing_app()->pack(); }
void TW_CALL tw_shuffle(void *clientData) { particle_packing_app()->shuffle(); }
void TW_CALL tw_print_pdf(void *clientData)
{
	std::cout << "Print to file MyFile.pdf ...";
	FILE *fp = fopen("MyFile.pdf", "wb");
	GLint buffsize = 0, state = GL2PS_OVERFLOW;
	GLint viewport[4];

	glGetIntegerv(GL_VIEWPORT, viewport);

	while( state == GL2PS_OVERFLOW ){ 
		buffsize += 1024*1024;//GL2PS_BSP_SORT 
		gl2psBeginPage ( "MyTitle", "MySoftware", viewport,
			GL2PS_PDF, GL2PS_BSP_SORT, GL2PS_SILENT |
			GL2PS_SIMPLE_LINE_OFFSET | GL2PS_NO_BLENDING |
			GL2PS_OCCLUSION_CULL | GL2PS_BEST_ROOT | 
			GL2PS_NO_PS3_SHADING | GL2PS_COMPRESS,
			GL_RGBA, 0, NULL, 2, 2, 2, buffsize,
			fp, "MyFile" );
		glut_viewer_redraw(); 
		state = gl2psEndPage();
	}

	fclose(fp);
	std::cout <<" End!" <<std::endl;
}
}

int main(int argc, char** argv) 
{
	//try
	//{
		Geex::initialize();
		Particle_packing::Particle_packing_application app(argc, argv) ;
		app.main_loop() ;
		Geex::terminate() ;
	//}
//	catch(std::exception& e)
//	{
//		std::cerr<<"Exception thrown : "<<e.what()<<std::endl;
//#ifdef _DEBUG
//		system("pause");
//#endif
//	}
//	catch(...)
//	{
//		std::cerr<<"Unknown exception thrown.\n";
//#ifdef _DEBUG
//		system("pause");
//#endif
//	}
	return 0;
}
